﻿#region Description
/*  File Name:      MECamera.cs
 *  Author:         Adam Goodchild
 *  
 *  Description:    This is our default Camera Class.
 *                  This is used to View our World.
*/
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MinimizeEngine.Entities;
using MinimizeEngine.Managers;
using Microsoft.Xna.Framework.Input;
#endregion

namespace MinimizeEngine.Components
{
    public class MECameraComponent : MEBaseComponent
    {
        #region Fields

        /// <summary>
        /// Cameras Viewport
        /// </summary>
        private Viewport mViewport;

        /// <summary>
        /// Current Position
        /// Defaults to Vector3.Zero
        /// </summary>
        private Vector3 mPosition = Vector3.Zero;

        /// <summary>
        /// The point the Camera is looking at
        /// </summary>
        private Vector3 mTarget = new Vector3(0, 0, -1);

        /// <summary>
        /// Cameras Up Vector
        /// </summary>
        private Vector3 mUp = Vector3.Up;

        /// <summary>
        /// Cameras Reference
        /// </summary>
        private Vector3 mReference = new Vector3(0.0f, 0.0f, 0.1f);

        /// <summary>
        /// Cameras Aspect Ratio
        /// </summary>
        private float mAspectRatio = 1.33333f;

        /// <summary>
        /// Cameras Near Plane
        /// </summary>
        private float mNearPlane = 0.1f;

        /// <summary>
        /// Cameras Far Plane
        /// </summary>
        private float mFarPlane = 3000.0f;

        /// <summary>
        /// Current Field of View
        /// </summary>
        private float mFOV = MathHelper.PiOver4;

        /// <summary>
        /// Level of Zoom for the Camera
        /// </summary>
        private int mZoom = 1;

        #endregion

        #region Properties

        /// <summary>
        /// Cameras Viewport
        /// </summary>
        public Viewport Viewport
        {
            get { return mViewport; }
            set { mViewport = value; }
        }

        /// <summary>
        /// Cameras Position
        /// </summary>
        public Vector3 Position
        {
            get { return mPosition; }
            set { mPosition = value; }
        }

        /// <summary>
        /// The Cameras Target - Where it is looking
        /// </summary>
        public Vector3 Target
        {
            get { return mTarget; }
            set { mTarget = value; }
        }

        /// <summary>
        /// Cameras Aspect Ratio
        /// </summary>
        public float AspectRatio
        {
            get { return mAspectRatio; }
            set { mAspectRatio = value; }
        }

        /// <summary>
        /// Default Field of View
        /// </summary>
        public float FOV
        {
            get { return mFOV; }
            set { mFOV = value; }
        }

        /// <summary>
        /// Level of Zoom for the Camera
        /// </summary>
        public int Zoom
        {
            get { return mZoom; }
            set { mZoom = value; }
        }

        /// <summary>
        /// Near Plane for the Camera
        /// </summary>
        public float NearPlane
        {
            get { return mNearPlane; }
            set { mNearPlane = value; }
        }

        /// <summary>
        /// Far Plane for the Camera
        /// </summary>
        public float FarPlane
        {
            get { return mFarPlane; }
            set { mFarPlane = value; }
        }

        /// <summary>
        /// Create a Ray from the Cameras Eye to the Mouse Cursors position
        /// </summary>
        /// <returns></returns>
        public Ray CreateMouseRay()
        {
            //Get the Current Mouse State.
            MouseState mState = Mouse.GetState();
            
            //Near Position, as close as possible to the Camera.
            Vector3 nearPos = new Vector3((float)mState.X, (float)mState.Y, 0f);
            //Far Position, as far away as possible.
            Vector3 farPos = new Vector3((float)mState.X, (float)mState.Y, 1f);

            Matrix world = Matrix.CreateTranslation(0, 0, 0);
            Matrix test = Matrix.Identity;

            //Now we find out where these two points are in World Space using Viewport.Unproject.
            Vector3 nearPoint = MEEngineManager.DeviceManager.GraphicsDevice.Viewport.Unproject(nearPos, Parent.Projection, Parent.View, world);
            Vector3 farPoint = MEEngineManager.DeviceManager.GraphicsDevice.Viewport.Unproject(farPos, Parent.Projection, Parent.View, world);

            Vector3 directionOfRay = farPoint - nearPoint;
            directionOfRay.Normalize();

            return new Ray(nearPoint, directionOfRay);
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Camera Constructor
        /// </summary>
        /// <param name="game"></param>
        public MECameraComponent(MEBaseEntity parent, float fov, float aspectRatio, float nearPlane, float farPlane) : base(parent)
        {
            mFOV = fov;
            mNearPlane = nearPlane;
            mFarPlane = farPlane;
            mAspectRatio = aspectRatio;

            Parent.BoundingFrustum = new BoundingFrustum(Parent.View * Parent.Projection);
            UpdateProjection();
        }

        #endregion

        #region Update

        /// <summary>
        /// Update the Camera
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            Parent.View = Matrix.CreateLookAt(Parent.Position, Parent.Position + Parent.Rotation.Forward, Parent.Rotation.Up);

            UpdateBoundingFrustum();

            UpdateProjection();
        }

        /// <summary>
        /// Update the Bounding Frustum
        /// </summary>
        protected void UpdateBoundingFrustum()
        {
            Parent.BoundingFrustum.Matrix = Parent.View * Parent.Projection;
        }

        /// <summary>
        /// Update the Projection Matrix
        /// </summary>
        protected void UpdateProjection()
        {
            Parent.Projection = Matrix.CreatePerspectiveFieldOfView(mFOV, mAspectRatio, mNearPlane, mFarPlane);
        }

        #endregion
    }
}
